Expand description

Isotest enables a very specific Rust unit testing pattern.

Say you have some complex data type which is used in a lot of places. You’d like to write functions that operate on that complex data, but these functions only need to know about some small subset of that data type. It could be convenient to write those functions generically over a trait which contains methods that work with that subset of data, rather than taking the full data type.

There a few key benefits to doing this. Using a trait hides irrelevant details, keeping the concern of the function limited to only what it needs to know. This is a good application of the principle of least privilege.

Using a trait also makes writing tests easier. If we used the full data type, we would need to create test fixtures with a lot of extraneous arbitrary data which won’t even affect the function under test. By using a trait, we can write a much simpler test-only data structure which implements the same trait, and use that instead of the complex “real” data type in our tests. This keeps tests simpler and avoids the need to generate lots of arbitrary throw-away data.

Additionally, when debugging a failing test, it’s a lot easier to get debug output on just the data we care about, and not have all of the noise of the real data structure included in the debug output.

The only concern with this approach is that we’re not testing the “real” data, and if there is any problem with our test data type or its implementation of the common trait, then we may not be testing what we think we are testing. This is where isotest comes in.

isotest helps ensure that our implementation of the common trait is correct by letting the user define functions to convert to and from the test data type, and then providing a macro with a simple API to run tests for both types. The user can write tests in terms of the simpler test data, but then that test gets run for both test and real data, to ensure that assumptions implicit in the trait implementation are not faulty. Thus, you get the benefit of simplifying your test writing while still testing your logic with real data.

Re-exports

pub use paste;

Macros

Helper to define a two-way Iso relationship between test data and real data.

Run the same closure for both the Test and Real versions of some data.

A version of isotest! which returns Future<Output = ()> instead of () and supports async syntax.

Structs

The API passed into an isotest in the Real context.

The API passed into an isotest in the Test context.

Enums

Which of the two contexts each isotest body will run under.

Traits

Trait that declares the relationship between a “test” and “real struct”, namely how to go back and forth between the two.

Functions

Test the invariants of your Iso implementation. This test must pass for any Real value you use.

Test the invariants of your Iso implementation. This test must pass for any Test value you use.

Roundtrip from real -> test -> real -> test, returning the two test items

Roundtrip from test -> real -> test

Type Definitions

Create function for the real context

Creation function for the test context

The argument to an isotest update function

Update function for the real context

Update function for the test context